iT邦幫忙

2025 iThome 鐵人賽

DAY 22
0
Software Development

30 天精通 C 語言建置與除錯:從 Makefile 到 CMake 跨平台實戰系列 第 22

[Day 22] [cmake] 深入了解Cmake指令與看懂CMakelist.txt

  • 分享至 

  • xImage
  •  

今天會學到的

  • 瞭解常用的 cmake 指令與選項(-S、-B、-G、-DCMAKE_BUILD_TYPE…)
  • 為什麼會產生 Makefile 或 build.ninja(Generator 的角色)
  • 逐行拆解一份簡單的 CMakeLists.txt

今天我們要回到[Day 20] [make→cmake] 執行人生中第一個CmakeList 中的專案,並詳細解釋Cmakelist.txt 之中的內容,以及運作的方式

首先我們先執行:

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug

注意,這邊沒有使用 -G 的指令,代表沒有指定generator 為 makefile的或ninja所以系統在建置的時候會使用預設的generator,而在大部分的Linux 或是 macOS 的環境下,預設的generator會是 Unix Makefiles 所以在不指定generator的情況下就會產生makefile了

如果你習慣使用 ninja 的話就可以指定 generatorninja的generator,例如:

cmake -S . -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Debug

下了上面的指令之後你就會看到 build/ 裡產生的是build.ninja 而不是 Makefile

另外,如果你想要自動執行編譯,而不是產生ninja或是makefile的話,可以用下面的指令

cmake --build build

總整理:
可以參考 [Day 21] [Cmake]深入解析Cmake, CMakelist.txt, Makefile 的對應關係下面表格中的指令對照, -B build 指定 cmakelist.txt 產生的相關makefile或是執行檔都要在 build 的這個資料夾中, -S 代表source (Cmakelist.txt)的來源,如果沒有特別指定的話就是指定當前的目錄,-G 就可以回頭看看上面的文章解釋

cmake -S . -B build -G "Unix Makefiles"   # 產生 Makefile
cmake -S . -B build -G "Ninja"            # 產生 build.ninja
cmake --build build                        # 直接編譯(自動挑 make 或 ninja)

對了這邊要補充說明一下CMake打在terminal的指令才會決定產生 Makefile/Ninja/VS/Xcode,而CMakeList.txt只有描述有哪些目標、來源、旗標、相依、安裝規則....

如果你還是不指定generator 下了 cmake -S . -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Debug的話,你就會看到terminal中出現下面的訊息,而專案資料夾出現一個新的build資料夾

https://ithelp.ithome.com.tw/upload/images/20250921/20178484eGuX6ltsYe.png
打開build/的資料夾,你會看到下面的資料型態,裡面有包含一個CMakeFiles的資料夾以及
下面的檔案目錄,其中你可以看到產生一個Makefile
https://ithelp.ithome.com.tw/upload/images/20250921/201784849N1S4RsS9B.png
而產生的Makefile裏頭的內容如下,基本上你可以不必管這個Makefile內有什麼內容了,因為CMake都會自動幫你產生,想要編譯的方式其實就好好標註在CMakelist.txt內就行了。

# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.28
# Default target executed when no arguments are given to make.
default_target: all

.PHONY : default_target

.......

這時,我們就可以回頭來看看這個CMakelist內有什麼東西了
以下會逐行解釋Cmakelist.txt中的內容

  • cmake_minimum_required(VERSION 3.21)
    指定此專案最低需要的 CMake 版本(3.21)。可避免在舊版 CMake 上因語法/功能不支援而出錯。

  • project(C_PROJ VERSION 1.0.0 LANGUAGES C)
    宣告專案名稱為 C_PROJ,版本 1.0.0,並且啟用 C 語言支援。CMake 會據此偵測 C 編譯器並初始化專案變數。

  • # Export compile_commands.json for clangd/clang-tidy
    註解:說明下面那行是為了輸出 compile_commands.json(給 clangd/clang-tidy/IDE 使用)。

  • set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
    開啟輸出 compile_commands.json。這份 JSON 會列出每個檔案的實際編譯指令,利於語意分析與靜態檢查。

  • # C standard
    註解:下面設定 C 語言標準。

  • set(CMAKE_C_STANDARD 11)
    指定 C11 為目標語言標準。

  • set(CMAKE_C_STANDARD_REQUIRED ON)
    要求必須使用指定標準(不要 fallback 到較舊的 C99 等)。

  • # Library from src/dog_meme.c
    註解:下面建立一個函式庫目標(由 src/dog_meme.c 組成)。

  • add_library(dog_meme STATIC
    建立名為 dog_meme 的 target,型別為 STATIC(靜態庫)。在 Unix 會輸出 libdog_meme.a

  • src/dog_meme.c
    指定此函式庫的來源檔(source file)。

  • )
    結束 add_library()

  • target_include_directories(dog_meme
    dog_meme 目標設定 include 路徑

  • PUBLIC
    指明此 include 路徑是 PUBLIC

    1. dog_meme 自己編譯時會使用;
    2. 任何連結到 dog_meme 的 target(例如等會的 app)也會自動繼承這個路徑。
  • ${CMAKE_SOURCE_DIR}/inc
    指出實際的 include 目錄在專案根下的 inc/。通常放標頭如 dog_meme.h

  • )
    結束 target_include_directories(dog_meme ...)

  • # Executable from src/main.c
    註解:下面建立可執行檔目標(由 src/main.c 組成)。

  • add_executable(app
    建立名為 app 的可執行檔 target。

  • src/main.c
    指定可執行檔的來源檔。

  • )
    結束 add_executable()

  • target_link_libraries(app PRIVATE dog_meme)
    app 連結 dog_meme 函式庫。PRIVATE 表示此相依性不會再向後傳遞給別的 target(只影響 app 自己)。

  • # Target-scoped warnings/defines/includes
    註解:接下來是對 app 這個 target 的專屬設定(警告、巨集、include)。

  • target_compile_options(app PRIVATE -Wall -Wextra -Wpedantic)
    app 加上編譯器警告選項(GCC/Clang 常見組合)。PRIVATE 表示只套用在 app 本身。

  • target_compile_definitions(app PRIVATE APP_VERSION="1.0.0")
    app 定義編譯期巨集 APP_VERSION="1.0.0";程式中可 printf("%s", APP_VERSION) 使用。

  • target_include_directories(app PRIVATE ${CMAKE_SOURCE_DIR}/inc)
    再對 app 額外加入 inc/ 為 include 路徑。

    註:由於 dog_meme 已用 PUBLIC 曝露 inc/,理論上 app 會繼承到;這一行屬於「保險/顯式」寫法。

  • # Install rules
    註解:下面是安裝規則。

  • install(TARGETS app RUNTIME DESTINATION bin)
    定義安裝 app 可執行檔到 bin/。之後可用:

    cmake --install <build_dir> --prefix <安裝根目錄>

安裝結果會出現在 <安裝根目錄>/bin/app
install(DIRECTORY inc/ DESTINATION include)
定義安裝 inc/ 目錄下的標頭檔到 include/。安裝後會是 <安裝根目錄>/include/...


上一篇
[Day 21] [Cmake]深入解析Cmake, CMakelist.txt, Makefile 的對應關係
下一篇
[Day 23] [gdb] 初探vscode gdb tool 的設定
系列文
30 天精通 C 語言建置與除錯:從 Makefile 到 CMake 跨平台實戰24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言